#pragma once

#include <queue>
#include <vector>
#include "Thread.hpp"
#include "Mutex.hpp"
#include "Cond.hpp"

const static int g_thread_num = 5;

// 单例线程池: 只能创建一个对象 - 懒汉模式
template <typename T>
class ThreadPool
{
private:
    bool QueueIsEmpty()
    {
        return _q.empty();
    }
    void Pop(const std::string &name)
    {
        while (true)
        {
            T t;
            {
                // 将共享资源获取到线程私有
                LockGuard lockguard(&_lock);
                while (QueueIsEmpty() && _running) // 队列为空且线程池运行状态不为false
                {
                    _wait_thread_num++;
                    _cond.Wait(_lock); // 条件变量等待
                    _wait_thread_num--;
                }
                if (!_running && QueueIsEmpty())
                {
                    LOG(LogLevel::INFO) << "线程池退出 && 任务队列为空" << name << "退出";
                    break;
                }
                // 此时一定有任务
                // 线程池退出就进行消耗历史任务
                // 线程池不退出就正常工作
                t = _q.front();
                _q.pop();
            }
            t(); // 任务执行不需要在临界区中
        }
    }

    ThreadPool(int thread_num = g_thread_num)
        : _running(false),
          _num(thread_num),
          _wait_thread_num(0)
    {
        for (int i = 0; i < _num; ++i)
        {
            std::string name = "thread-" + std::to_string(i + 1);
            _threads.emplace_back([this](const std::string &name)
                                  { this->Pop(name); }, name);
        }
        LOG(LogLevel::INFO) << "create threadpool success";
    }

    //防止赋值
    ThreadPool<T> &operator=(const ThreadPool<T> &) = delete;
    ThreadPool(const ThreadPool<T> &) = delete;

public:
    void Start()
    {
        if (_running)
            return;
        _running = true;
        for (auto &t : _threads)
        {
            t.Start(); // 线程启动
        }
        LOG(LogLevel::INFO) << "Start threadpool success";
    }

    //  线程按照正常唤醒逻辑退出
    //  1、线程池被唤醒，且没有任务执行 == 线程池退出
    //  2、线程池被唤醒，且有任务执行 == 线程池不会立即退出，要等任务完成再退出
    //  3、线程没有休眠，将它要执行的任务都执行完再退出
    //  队列中有任务的情况下，就不会休眠
    void Stop()
    {
        if (!_running)
            return;
        _running = false;
        if (_wait_thread_num) // 有线程在休眠就全部唤醒
            _cond.NotifyAll();

        LOG(LogLevel::INFO) << "Stop threadpool success";
    }

    void Wait()
    {
        for (auto &t : _threads)
        {
            t.Join(); // 线程等待
        }
        LOG(LogLevel::INFO) << "Wait threadpool success";
    }

    void Push(const T &in)
    {
        if (!_running)
            return;
        {
            LockGuard lockguard(&_lock);

            _q.push(in);
            if (_wait_thread_num > 0)
                _cond.NotifyOne();
        }
    }

    //获取线程池地址
    static std::string ToHex(ThreadPool<T> *addr)
    {
        char buffer[64];
        snprintf(buffer, 64, "%p", addr);
        return buffer;
    }

    // 笔记4: 获取单例
    // 在构造函数私有化后就不能在类外创建对象，所以在不加static的前提下就无法调用该函数
    // 所以该方法就必须是static, 因此_instance也是static, 锁也是static
    // 注: static成员函数要直接访问类内属性就需要在类中实现
    static ThreadPool<T> *GetInstance()
    {
        {
            if (!_instance)
            {
                LockGuard lockguard(&_singleton_lock); // 保护多线程对线程池的访问, 提高效率
                if (!_instance)
                {
                    _instance = new ThreadPool<T>(); // 延迟加载, 要用的时候再申请
                    LOG(LogLevel::DEBUG) << "线程池单例首次被使用, 创建并初始化, addr: " << ToHex(_instance);
                    _instance->Start();
                }
            }
            else
            {
                LOG(LogLevel::DEBUG) << "线程池已存在, 直接获取" << ToHex(_instance);
            }
        }
        return _instance;
    }

    ~ThreadPool()
    {
    }

private:
    std::queue<T> _q;             // 任务队列
    std::vector<Thread> _threads; // 多个线程
    int _num;                     // 线程数

    int _wait_thread_num; // 等待的线程数

    Mutex _lock;
    Cond _cond;

    bool _running; // 运行状态

    // 笔记4: 单例静态指针
    static ThreadPool<T> *_instance;
    static Mutex _singleton_lock;
};

// 静态成员变量初始化
template <typename T>
ThreadPool<T> *ThreadPool<T>::_instance = nullptr; 

template <typename T>
Mutex ThreadPool<T>::_singleton_lock;